#include "EZListener.h"
#include "loop/EZLoop.h"
#include "conn/EZSocketFD.h"
#include "common/EZLog.h"
#include "common/EZUtil.h"
#include "event.h"
#include "event2/util.h"
#include <assert.h>



namespace EZTcp{


EZListener::EZListener(EZLoop* loop, int port)
	: loop_(loop)
	, port_(port)
	, fd_(-1)
{

}


EZListener::~EZListener()
{
	EZLOG_DEBUG << "fd=" << chan_->fd();
	chan_.reset();
	evutil_closesocket(fd_);
	fd_ = -1;
}

void EZListener::listen()
{
	EZLOG_DEBUG;
	fd_ = EZUtil::create_non_blockint_socket();
	if (fd_ < 0) {
		int serrno = errno;
		EZLOG_FATAL << "Create a nonblocking socket failed "
			<< EZUtil::strerror(serrno);
		return;
	}

	struct ::sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port_);
	
	// TODO Add retry when failed
	int ret = ::bind(fd_, (struct sockaddr*)&addr, static_cast<socklen_t>(sizeof addr));
	if (ret < 0) {
		int serrno = errno;
		EZLOG_FATAL << "bind error :" << EZUtil::strerror(serrno);
	}
    EZUtil::local_socket_addr(fd_, listen_addr_, port_);
	ret = ::listen(fd_, 0x7fffffff);
	if (ret < 0) {
		int serrno = errno;
		EZLOG_FATAL << "Listen failed " << EZUtil::strerror(serrno);
	}
}

void EZListener::accept()
{
	EZLOG_DEBUG;
	chan_.reset(new EZSocketFD(loop_, fd_, true, false));
	chan_->set_read_callback(std::bind(&EZListener::_handle_accept, this));
	loop_->dispatch(std::bind(&EZSocketFD::attach_to_loop, chan_.get()));
	EZLOG_INFO << "TCPServer is running at " << port_;
}

void EZListener::stop()
{
	assert(loop_->is_in_loop_thread());
	chan_->disable_all_event();
	chan_->destroy();
}

void EZListener::_handle_accept()
{
	EZLOG_DEBUG << "A new connection is coming in";
	assert(loop_->is_in_loop_thread());

	auto nfd = accept_fn_(loop_, fd_, new_conn_fn_);
	if(nfd == -1){
		EZLOG_ERROR << "accept error";
		return;
	}

	EZUtil::set_keepalive(nfd, true);

}

}
